/**
 * HTML内容安全渲染工具
 * 提供安全的HTML内容渲染功能，支持图片、表格等富媒体内容
 */

/**
 * HTML标签白名单 - 只允许安全的标签
 */
const ALLOWED_TAGS = new Set([
  // 文本格式标签
  'p',
  'br',
  'span',
  'div',
  'strong',
  'b',
  'em',
  'i',
  'u',
  'del',
  'ins',
  'h1',
  'h2',
  'h3',
  'h4',
  'h5',
  'h6',
  // 列表标签
  'ul',
  'ol',
  'li',
  'dl',
  'dt',
  'dd',
  // 表格标签
  'table',
  'thead',
  'tbody',
  'tr',
  'td',
  'th',
  'caption',
  'colgroup',
  'col',
  // 图片标签
  'img',
  // 链接标签
  'a',
  // 其他安全标签
  'hr',
  'blockquote',
  'pre',
  'code'
])

/**
 * HTML属性白名单 - 只允许安全的属性
 */
const ALLOWED_ATTRIBUTES = new Map([
  // 通用属性
  ['class', true],
  ['id', true],
  ['style', true],
  ['title', true],
  ['lang', true],
  ['dir', true],

  // 图片属性
  ['src', (value) => isValidUrl(value)],
  ['alt', true],
  ['width', (value) => isValidDimension(value)],
  ['height', (value) => isValidDimension(value)],

  // 链接属性
  ['href', (value) => isValidUrl(value)],
  ['target', (value) => ['_blank', '_self', '_parent', '_top'].includes(value)],
  ['rel', (value) => value === 'noopener noreferrer'],

  // 表格属性
  ['colspan', (value) => isValidNumber(value)],
  ['rowspan', (value) => isValidNumber(value)],
  ['scope', (value) => ['row', 'col', 'rowgroup', 'colgroup'].includes(value)],

  // 列表属性
  ['start', (value) => isValidNumber(value)],
  ['type', (value) => ['1', 'a', 'A', 'i', 'I'].includes(value)]
])

/**
 * CSS属性白名单
 */
const ALLOWED_CSS_PROPERTIES = new Set([
  // 布局属性
  'display',
  'position',
  'top',
  'right',
  'bottom',
  'left',
  'z-index',
  'float',
  'clear',
  'overflow',
  'overflow-x',
  'overflow-y',

  // 尺寸属性
  'width',
  'height',
  'min-width',
  'max-width',
  'min-height',
  'max-height',
  'margin',
  'margin-top',
  'margin-right',
  'margin-bottom',
  'margin-left',
  'padding',
  'padding-top',
  'padding-right',
  'padding-bottom',
  'padding-left',
  'border',
  'border-top',
  'border-right',
  'border-bottom',
  'border-left',
  'border-width',
  'border-style',
  'border-color',
  'border-radius',

  // 文本属性
  'font-family',
  'font-size',
  'font-weight',
  'font-style',
  'font-variant',
  'text-align',
  'text-decoration',
  'text-transform',
  'text-indent',
  'line-height',
  'letter-spacing',
  'word-spacing',
  'white-space',
  'color',
  'background-color',
  'opacity',

  // 表格属性
  'border-collapse',
  'border-spacing',
  'empty-cells',
  'caption-side',
  'table-layout',

  // 其他属性
  'visibility',
  'cursor',
  'box-sizing'
])

/**
 * 验证URL是否安全
 * @param {string} url
 * @returns {boolean}
 */
function isValidUrl(url) {
  if (!url || typeof url !== 'string') return false

  // 移除危险协议
  const lowerUrl = url.toLowerCase()
  if (
    lowerUrl.startsWith('javascript:') ||
    lowerUrl.startsWith('data:') ||
    lowerUrl.startsWith('vbscript:') ||
    lowerUrl.startsWith('file:')
  ) {
    return false
  }

  // 允许相对路径和安全的协议
  return /^(https?:\/\/|\/|\.\.\/|\.\/|[a-zA-Z0-9\-._~!$&'()*+,;=:@%?]*)/.test(url)
}

/**
 * 验证尺寸值是否安全
 * @param {string} value
 * @returns {boolean}
 */
function isValidDimension(value) {
  if (!value || typeof value !== 'string') return false
  return /^(auto|0|[1-9]\d*px|[1-9]\d*%|[1-9]\d*rem|[1-9]\d*em)$/.test(value.trim())
}

/**
 * 验证数字值是否安全
 * @param {string} value
 * @returns {boolean}
 */
function isValidNumber(value) {
  if (!value || typeof value !== 'string') return false
  return /^[1-9]\d*$/.test(value.trim())
}

/**
 * 过滤CSS样式
 * @param {string} styleText
 * @returns {string}
 */
function filterCssStyle(styleText) {
  if (!styleText || typeof styleText !== 'string') return ''

  const rules = styleText.split(';')
  const filteredRules = []

  for (const rule of rules) {
    const trimmedRule = rule.trim()
    if (!trimmedRule) continue

    const [property, value] = trimmedRule.split(':').map((s) => s.trim())

    if (ALLOWED_CSS_PROPERTIES.has(property)) {
      filteredRules.push(`${property}: ${value}`)
    }
  }

  return filteredRules.join('; ')
}

/**
 * 过滤HTML属性
 * @param {string} tagName
 * @param {Record<string, string>} attributes
 * @returns {Record<string, string>}
 */
function filterAttributes(tagName, attributes) {
  const filteredAttributes = {}

  for (const [attrName, attrValue] of Object.entries(attributes)) {
    const config = ALLOWED_ATTRIBUTES.get(attrName)

    if (config === true) {
      // 允许所有值
      filteredAttributes[attrName] = attrValue
    } else if (typeof config === 'function') {
      // 需要验证的值
      if (config(attrValue)) {
        filteredAttributes[attrName] = attrValue
      }
    }
  }

  // 特殊处理：为外部链接添加安全属性
  if (tagName === 'a' && filteredAttributes.href) {
    const href = filteredAttributes.href.toLowerCase()
    if (href.startsWith('http://') || href.startsWith('https://')) {
      filteredAttributes.rel = 'noopener noreferrer'
      filteredAttributes.target = '_blank'
    }
  }

  // 特殊处理：为图片添加alt文本
  if (tagName === 'img' && !filteredAttributes.alt) {
    filteredAttributes.alt = ''
  }

  return filteredAttributes
}

/**
 * 清理HTML内容
 * @param {string} html
 * @returns {string}
 */
export function sanitizeHtml(html) {
  if (!html || typeof html !== 'string') return ''

  // 使用DOM API进行HTML清理
  const parser = new DOMParser()
  const doc = parser.parseFromString(`<div>${html}</div>`, 'text/html')
  const container = doc.querySelector('div')

  if (!container) return ''

  return sanitizeNode(container).innerHTML
}

/**
 * 递归清理DOM节点
 * @param {Node} node
 * @returns {Node}
 */
function sanitizeNode(node) {
  if (node.nodeType === Node.TEXT_NODE) {
    return node
  }

  if (node.nodeType !== Node.ELEMENT_NODE) {
    return document.createTextNode('')
  }

  const tagName = node.tagName.toLowerCase()

  // 检查标签是否允许
  if (!ALLOWED_TAGS.has(tagName)) {
    // 移除不允许的标签，但保留其内容
    const fragment = document.createDocumentFragment()
    while (node.firstChild) {
      fragment.appendChild(sanitizeNode(node.firstChild))
    }
    return fragment
  }

  // 创建新的元素
  const newNode = document.createElement(tagName)

  // 过滤属性
  const attributes = {}
  for (const attr of node.attributes) {
    attributes[attr.name] = attr.value
  }

  const filteredAttributes = filterAttributes(tagName, attributes)
  for (const [attrName, attrValue] of Object.entries(filteredAttributes)) {
    if (attrName === 'style') {
      const filteredStyle = filterCssStyle(attrValue)
      if (filteredStyle) {
        newNode.setAttribute(attrName, filteredStyle)
      }
    } else {
      newNode.setAttribute(attrName, attrValue)
    }
  }

  // 递归处理子节点
  while (node.firstChild) {
    newNode.appendChild(sanitizeNode(node.firstChild))
  }

  return newNode
}

/**
 * 渲染安全的HTML内容
 * @param {string} content
 * @param {Object} options
 * @returns {string}
 */
export function renderSafeHtml(content, options = {}) {
  if (!content || typeof content !== 'string') return ''

  const {
    maxContentLength = 10000, // 最大内容长度
    allowedTags = ALLOWED_TAGS,
    stripComments = true // 是否移除注释
  } = options

  // 检查内容长度
  if (content.length > maxContentLength) {
    console.warn(`HTML content exceeds maximum length of ${maxContentLength} characters`)
    content = content.substring(0, maxContentLength)
  }

  // 移除HTML注释
  if (stripComments) {
    content = content.replace(/<!--[\s\S]*?-->/g, '')
  }

  try {
    return sanitizeHtml(content)
  } catch (error) {
    console.error('HTML sanitization failed:', error)
    // 返回转义后的文本作为降级方案
    return escapeHtml(content)
  }
}

/**
 * 转义HTML特殊字符
 * @param {string} text
 * @returns {string}
 */
function escapeHtml(text) {
  if (!text || typeof text !== 'string') return ''

  const div = document.createElement('div')
  div.textContent = text
  return div.innerHTML
}

/**
 * 检测内容类型
 * @param {string} content
 * @returns {string}
 */
export function detectContentType(content) {
  if (!content || typeof content !== 'string') return 'text'

  const lowerContent = content.toLowerCase()

  if (lowerContent.includes('<img')) return 'image'
  if (lowerContent.includes('<table')) return 'table'
  if (lowerContent.includes('<a') && lowerContent.includes('href=')) return 'link'
  if (lowerContent.includes('<h1') || lowerContent.includes('<h2') || lowerContent.includes('<h3'))
    return 'heading'
  if (lowerContent.includes('<ul') || lowerContent.includes('<ol')) return 'list'

  return 'text'
}

/**
 * 导出默认实例
 */
export default {
  renderSafeHtml,
  sanitizeHtml,
  detectContentType,
  escapeHtml,
  ALLOWED_TAGS,
  ALLOWED_ATTRIBUTES,
  ALLOWED_CSS_PROPERTIES
}
